﻿using KzwgRoomTester;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

public delegate void PackageHandler(PacketObject packet);
public class Session
{

    public enum ConnectType { TCP, UDP }
    // private static volatile Session m_instance = null;
    private static readonly object locker = new object();
    private static volatile Dictionary<string, Session> sessions = new Dictionary<string, Session>();
    private static Dictionary<ushort, PackageHandler> packetHandlers = new Dictionary<ushort, PackageHandler>();


    public static Session GetTCPSessionInstance(string host, ushort port)
    {
        return GetSessionInstance(host, port, ConnectType.TCP);
    }

    public static Session GetUDPSessionInstance(string host, ushort port)
    {
        return GetSessionInstance(host, port, ConnectType.UDP);
    }


    public static Session GetSessionInstance(string host, ushort port, ConnectType connectType)
    {
        var sKey = sessionKey(host, port, connectType);
        var sess = sessions.Get(sKey);
        if (sess != null)
        {
            return sess;
        }

        lock (locker)
        {
            sess = sessions.Get(sKey);
            if (sess == null)
            {
                sess = new Session();
                sess.m_host = host;
                sess.m_port = port;
                if (connectType == ConnectType.TCP)
                {
                    sess.m_conn = new TCPConnection();
                }
                else
                {
                    sess.m_conn = new UDPConnection();
                }

            }
        }

        return sess;
    }

    private static string sessionKey(string host, ushort port, ConnectType connectType)
    {
        return string.Format("{0}:{1}:{2}", connectType, host, port);
    }

    public void addPackageHandler(ushort ID, PackageHandler handler)
    {
        if (packetHandlers.ContainsKey(ID))
        {
            packetHandlers[ID] += handler;
        }
        else
        {
            packetHandlers[ID] = new PackageHandler(handler);
        }
    }


    public void removePackageHandler(ushort ID, Action<PacketObject> handler)
    {
        if (packetHandlers.ContainsKey(ID))
        {
            packetHandlers[ID] -= new PackageHandler(handler);
        }
    }


    public string host { get { return m_host; } }
    public int port { get { return m_port; } }


    private PacketSender m_sender;
    private PacketReceiver m_receiver;
    private IConnection m_conn;
    private Action<Session> m_onConnect;

    private string m_host;
    private int m_port;
    //private Thread m_thread;



    private Session()
    {
    }


    private Session(string host, ushort port) : this()
    {
        m_host = host;
        m_port = port;
    }



    public void SendPing()
    {
        Send(PacketObject.Create<CsPing>());
    }




    public void Connect(Action<Session> onConnect = null)
    {
        if (m_conn.IsConnected) { return; }
        if (onConnect != null) { m_onConnect += onConnect; }

        Logger.LogInfo("connecting {0}:{1}", m_host, m_port);
        m_conn.Connect(host, port);
        m_sender = new PacketSender(m_conn);
        m_receiver = new PacketReceiver(m_conn);
        //m_thread = new Thread(doReceivePacket);
        //m_thread.Start();
        //new Task(doReceivePacket).Start();
        Task.Run(doReceivePacket);
        //TaskManager.AddTask(doReceivePacket, 10);
    }



    public void Disconnect()
    {
        Logger.LogInfo("Disconnect from server {0}:{1}", host, port);
        if (m_conn == null || !m_conn.IsConnected)
        {
            return;
        }
        m_conn.Close();
        m_conn = null;

        if (m_receiver != null) m_receiver.Destroy();
        if (m_sender != null) m_sender.Destroy();

        m_sender = null;
        m_receiver = null;
        m_onConnect = null;

    }

    public virtual void Send(PacketObject packet)
    {
        var p = packet.BuildPacket();
        m_sender.Send(p);
        if (!packet.dontDestroyOnSend) packet.Destroy();
    }


    private async Task doReceivePacket()
    {
        List<PacketObject> packetList;
        while (true)
        {

            if (m_receiver != null)
            {
                packetList = m_receiver.ReceivedPackets();

                foreach (var p in packetList)
                {
                    HandlePacket(p);
                    await Task.Yield();
                }
                packetList.Clear();

            }

            if ((m_conn == null || !m_conn.IsConnected))
            {
                Disconnect();
                break;
            }
            await Task.Delay(10);
        }
    }


    public void HandlePacket(PacketObject p)
    {
        if (p == null) return;
        var handler = packetHandlers.Get(p._ID);
        if (handler == null)
        {
            p.Destroy();
            return;
        }

        handler.Invoke(p);
        //await Task.Factory.FromAsync(handler.BeginInvoke, (result) => {
        //    if (!p.dontDestroyOnRecv) p.Destroy();
        //}, p, p,   TaskCreationOptions.None);



    }


}